package com.hzw.saas.web.im.im.websocket;

import cn.hutool.core.collection.CollectionUtil;
import com.hzw.saas.api.chat.IChatEventService;
import com.hzw.saas.api.chat.IChatUserGroupService;
import com.hzw.saas.api.chat.bo.ChatEventBO;
import com.hzw.saas.api.chat.bo.ChatUserGroupBO;
import com.hzw.saas.common.config.bo.PayLoad;
import com.hzw.saas.common.config.msg.ChatMsg;
import com.hzw.saas.common.security.utils.SecurityUtils;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.web.im.im.ImContext;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.*;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author sonam
 * @sine 2021/10/21 9:54 上午
 */
@Slf4j
@Component
@ChannelHandler.Sharable
public class WebSocketImHandler extends SimpleChannelInboundHandler<WebSocketFrame> { //接受frame会使握手协议失效
    @Resource
    private IChatEventService chatEventService;
    @Resource
    private IChatUserGroupService chatUserGroupService;


    /**
     * 当有客户端连接时，执行，将连上的客户端记录下来
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) {
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) {
        Channel channel = ctx.channel();
        try {
            ImContext.userChannel.remove(SecurityUtils.getUserId());
        } catch (Exception ignore) { /*~ignore*/ }
        ImContext.webSocketHandShakerMap.remove(channel.id().asLongText());
        log.info("断开连接{}，当前连接数为：{}", channel.id().asLongText(), ImContext.webSocketHandShakerMap.size());
    }

    /**
     * 客户端发送消息时执行
     *
     * @param ctx
     * @param
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, WebSocketFrame frame) {
        handlerWebsocketFrame(ctx, frame);
    }

    private void handlerWebsocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
        Channel channel = ctx.channel();
        // 关闭请求
        if (frame instanceof CloseWebSocketFrame) {
            WebSocketServerHandshaker handshaker =
                ImContext.webSocketHandShakerMap.get(ctx.channel().id().asLongText());
            if (handshaker == null) {
                sendErrorMessage(ctx, "不存在的客户端连接！");
            } else {
                handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            }
            return;
        }
        // ping请求
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }

        if (frame instanceof TextWebSocketFrame) {
            TextWebSocketFrame request = (TextWebSocketFrame) frame;
            log.info("+++++++++++++++4"+request.text());
            if (request.text().equals("*PING*")){
                ctx.channel().writeAndFlush(new TextWebSocketFrame("*PONG*"));
                return;
            }
            log.info("+++++++0"+request.text());
            JSONObject jsonObject = JSONObject.fromObject(request.text());
            ChatMsg chatMsg = (ChatMsg) JSONObject.toBean(jsonObject, ChatMsg.class);
            String userId = SecurityUtils.getUser().getUserId();
            //拿到实际信息包
            PayLoad payLoad = chatMsg.getPayLoad();
            payLoad.setCreatorId(userId);
            payLoad.setCreateTime(new Date());
            //数据入库
            saveChatMsg(payLoad);
            String targetGroupId = payLoad.getTargetGroupId();
            List<ChatUserGroupBO> usersByGroupId = chatUserGroupService.findUsersByGroupId(targetGroupId);
            if (CollectionUtil.isNotEmpty(usersByGroupId)) {
                //需要推送数据的用户id
                List<String> sendUserIds = usersByGroupId.stream().map(ChatUserGroupBO::getUserId).collect(Collectors.toList());
                List<Channel> channelArrayList = new ArrayList<>();
                sendUserIds.forEach(targetuserId -> {
                    Channel userCh = ImContext.userChannel.get(targetuserId);
                    if (userCh != null) {
                        channelArrayList.add(userCh);
                    }
                });
                //清空集合
                sendUserIds.clear();
                for (Channel ch : channelArrayList) {
                    if (ch != channel) {
                        ch.writeAndFlush(new TextWebSocketFrame(request.text()));
                        log.info("+++++++++++++1"+request.text());
                    } else {
                        ch.writeAndFlush(new TextWebSocketFrame(request.text()));
                        log.info("+++++++++++++2"+request.text());
                    }
                }
                //清空集合
                channelArrayList.clear();
            }
        }
    }

    private void saveChatMsg(PayLoad payLoad) {
        if (payLoad == null) {
            return;
        }
        ChatEventBO map = MapperUtil.nf().map(payLoad, ChatEventBO.class);
        map.setEventType(41);
        chatEventService.addEvent(map);
    }

    /**
     * 发送错误消息
     *
     * @param ctx
     * @param errorMsg
     */
    private void sendErrorMessage(ChannelHandlerContext ctx, String errorMsg) {
        ctx.channel().writeAndFlush(new TextWebSocketFrame(errorMsg));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }

}
